﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web;
using System.Web.Mvc.Html;
using MvcEngine.Core.Validation;
using System.Web.Mvc.Ajax;
using System.Globalization;
using MvcEngine.Core.Helpers;
using MvcEngine.Mvc.Models;

using MvcEngine.Core.Content;
using System.Linq.Expressions;
using System.Reflection;
using MvcEngine.Mvc.UI;
using System.IO;
using MvcEngine.Core.Utils;
using MvcEngine.Core;
using MvcEngine.Core.Localization;
using MvcEngine.Mvc.Models.ViewModels;

namespace MvcEngine.Mvc.Extensions
{
    public static class HtmlHelperExtensions
    {
        #region Script

        public static string GetScriptTag(this HtmlHelper htmlHelper, string path)
        {
            Guard.ThrowIfEmptyString(path);
            if (!(path.StartsWith("http://") || path.StartsWith("https://")))
            {
                UrlHelper urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
                path = !path.StartsWith("/") ? urlHelper.ScriptPath(path) : urlHelper.AppPath(path);
            }
            return htmlHelper.ScriptBlock("text/javascript", path);
        }

        public static void RenderScriptTag(this HtmlHelper htmlHelper, string path)
        {
            htmlHelper.ViewContext.HttpContext.Response.Write(htmlHelper.GetScriptTag(path));
        }

        public static string ScriptBlock(this HtmlHelper htmlHelper, string type, string src)
        {
            return htmlHelper.ScriptBlock(type, src, null);
        }

        public static string ScriptBlock(this HtmlHelper htmlHelper, string type, string src, object htmlAttributes)
        {
            TagBuilder tagBuilder = new TagBuilder("script");

            tagBuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
            if (!string.IsNullOrEmpty(type))
            {
                tagBuilder.MergeAttribute("type", type);
            }
            if (!string.IsNullOrEmpty(src))
            {
                tagBuilder.MergeAttribute("src", src);
            }

            return tagBuilder.ToString(TagRenderMode.Normal);
        }

        #endregion

        #region Css

        public static string GetCssFile(this HtmlHelper htmlHelper, string path)
        {
            Guard.ThrowIfEmptyString(path);
            if (!(path.StartsWith("http://") || path.StartsWith("https://")))
            {
                UrlHelper urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
                path = !path.StartsWith("/") ? urlHelper.CssPath(path) : urlHelper.AppPath(path);
            }
            return htmlHelper.HeadLink("stylesheet", path, "text/css", "");
        }

        public static void RenderCssFile(this HtmlHelper htmlHelper, string path)
        {
            htmlHelper.ViewContext.HttpContext.Response.Write(htmlHelper.GetCssFile(path));
        }

        #endregion

        #region HeadLink

        public static string HeadLink(this HtmlHelper htmlHelper, string rel, string href, string type, string title)
        {
            return htmlHelper.HeadLink(rel, href, type, title, null);
        }

        public static string HeadLink(this HtmlHelper htmlHelper, string rel, string href, string type, string title, object htmlAttributes)
        {
            TagBuilder tagBuilder = new TagBuilder("link");

            tagBuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
            if (!string.IsNullOrEmpty(rel))
            {
                tagBuilder.MergeAttribute("rel", rel);
            }
            if (!string.IsNullOrEmpty(href))
            {
                tagBuilder.MergeAttribute("href", href);
            }
            if (!string.IsNullOrEmpty(type))
            {
                tagBuilder.MergeAttribute("type", type);
            }
            if (!string.IsNullOrEmpty(title))
            {
                tagBuilder.MergeAttribute("title", title);
            }

            return tagBuilder.ToString(TagRenderMode.SelfClosing);
        }

        #endregion

        #region UrlActionLink

        public static string PostActionLink<TModel>(this HtmlHelper<TModel> htmlHelper, string linkText, string url)
            where TModel : MvcEngineModel
        {
            return htmlHelper.PostActionLink(linkText, url, string.Empty, null, true);
        }

        public static string PostActionLink<TModel>(this HtmlHelper<TModel> htmlHelper, string linkText, string url, bool encode)
            where TModel : MvcEngineModel
        {
            return htmlHelper.PostActionLink(linkText, url, string.Empty, null, encode);
        }

        public static string PostActionLink<TModel>(this HtmlHelper<TModel> htmlHelper, string linkText, string url, object htmlAttributes)
            where TModel : MvcEngineModel
        {
            return htmlHelper.PostActionLink(linkText, url, string.Empty, new RouteValueDictionary(htmlAttributes), true);
        }

        public static string PostActionLink<TModel>(this HtmlHelper<TModel> htmlHelper, string linkText, string url, object htmlAttributes, bool encode)
            where TModel : MvcEngineModel
        {
            return htmlHelper.PostActionLink(linkText, url, string.Empty, new RouteValueDictionary(htmlAttributes), encode);
        }

        public static string PostActionLink<TModel>(this HtmlHelper<TModel> htmlHelper, string linkText, string url, RouteValueDictionary htmlAttributes)
            where TModel : MvcEngineModel
        {
            return htmlHelper.PostActionLink(linkText, url, string.Empty, htmlAttributes, true);
        }

        public static string PostActionLink<TModel>(this HtmlHelper<TModel> htmlHelper, string linkText, string url, RouteValueDictionary htmlAttributes, bool encode)
            where TModel : MvcEngineModel
        {
            return htmlHelper.PostActionLink(linkText, url, string.Empty, htmlAttributes, encode);
        }

        public static string PostActionLink<TModel>(this HtmlHelper<TModel> htmlHelper, string linkText, string url, string javaScript)
            where TModel : MvcEngineModel
        {
            return htmlHelper.PostActionLink(linkText, url, javaScript, null, true);
        }

        public static string PostActionLink<TModel>(this HtmlHelper<TModel> htmlHelper, string linkText, string url, string javaScript, bool encode)
            where TModel : MvcEngineModel
        {
            return htmlHelper.PostActionLink(linkText, url, javaScript, null, encode);
        }

        public static string PostActionLink<TModel>(this HtmlHelper<TModel> htmlHelper, string linkText, string url, string javaScript, RouteValueDictionary htmlAttributes, bool encode)
            where TModel : MvcEngineModel
        {
            string formId = "form" + Guid.NewGuid();
            javaScript = javaScript.Replace("{0}", formId);

            using (htmlHelper.BeginForm(url, new { id = formId }))
            {
                TagBuilder builder = new TagBuilder("a");
                builder.MergeAttribute("onclick", javaScript);
                builder.MergeAttribute("href", "javascript:void(0);");
                builder.MergeAttributes<string, object>(htmlAttributes);

                builder.InnerHtml = !string.IsNullOrEmpty(linkText) ? encode ? HttpUtility.HtmlEncode(linkText) : linkText : string.Empty;
                htmlHelper.ViewContext.HttpContext.Response.Write(builder.ToString(TagRenderMode.Normal));
                htmlHelper.ViewContext.HttpContext.Response.Write(htmlHelper.MvcEngineAntiForgeryToken(m => m.AntiForgeryToken));
            }
            return string.Empty;
        }




        public static void SubmitActionLink(this HtmlHelper htmlHelper, string linkText, string javaScript)
        {
            htmlHelper.SubmitActionLink(linkText, javaScript, null);
        }

        public static void SubmitActionLink(this HtmlHelper htmlHelper, string linkText, string javaScript, RouteValueDictionary htmlAttributes)
        {
            TagBuilder builder = new TagBuilder("a");
            builder.MergeAttribute("onclick", javaScript + "$(this).parent().submit();");
            builder.MergeAttribute("value", linkText);
            builder.MergeAttribute("href", "javascript:void(0);");
            builder.MergeAttributes<string, object>(htmlAttributes);
            builder.InnerHtml = htmlHelper.Encode(linkText);
            htmlHelper.ViewContext.HttpContext.Response.Write(builder.ToString(TagRenderMode.Normal));
        }

        #region UrlActionLink

        public static string UrlActionLink(this HtmlHelper htmlHelper, string linkText, string url)
        {
            return htmlHelper.UrlActionLink(linkText, url, new RouteValueDictionary(), true, null);
        }

        public static string UrlActionLink(this HtmlHelper htmlHelper, string linkText, string url, string javascript)
        {
            return htmlHelper.UrlActionLink(linkText, url, new RouteValueDictionary(), true, javascript);
        }

        public static string UrlActionLink(this HtmlHelper htmlHelper, string linkText, string url, string javascript, bool encode)
        {
            return htmlHelper.UrlActionLink(linkText, url, new RouteValueDictionary(), encode, javascript);
        }

        public static string UrlActionLink(this HtmlHelper htmlHelper, string linkText, string url, bool encode)
        {
            return htmlHelper.UrlActionLink(linkText, url, new RouteValueDictionary(), encode, null);
        }

        public static string UrlActionLink(this HtmlHelper htmlHelper, string linkText, string url, object htmlAttributes)
        {
            return htmlHelper.UrlActionLink(linkText, url, new RouteValueDictionary(htmlAttributes), true, null);
        }

        public static string UrlActionLink(this HtmlHelper htmlHelper, string linkText, string url, object htmlAttributes, string javascript)
        {
            return htmlHelper.UrlActionLink(linkText, url, new RouteValueDictionary(htmlAttributes), true, javascript);
        }

        public static string UrlActionLink(this HtmlHelper htmlHelper, string linkText, string url, object htmlAttributes, string javascript, bool encode)
        {
            return htmlHelper.UrlActionLink(linkText, url, new RouteValueDictionary(htmlAttributes), encode, javascript);
        }

        public static string UrlActionLink(this HtmlHelper htmlHelper, string linkText, string url, object htmlAttributes, bool encode)
        {
            return htmlHelper.UrlActionLink(linkText, url, new RouteValueDictionary(htmlAttributes), encode, null);
        }

        public static string UrlActionLink(this HtmlHelper htmlHelper, string linkText, string url, RouteValueDictionary htmlAttributes, bool encode, string javascript)
        {
            TagBuilder builder2 = new TagBuilder("a");
            builder2.InnerHtml = !string.IsNullOrEmpty(linkText) ? encode ? HttpUtility.HtmlEncode(linkText) : linkText : string.Empty;
            TagBuilder builder = builder2;
            builder.MergeAttributes<string, object>(htmlAttributes);

            if (!string.IsNullOrEmpty(javascript))
            {
                builder.MergeAttribute("onclick", javascript);
                builder.MergeAttribute("href", "javascript:void(0);");
            }
            else
            {
                builder.MergeAttribute("href", url);
            }
            return builder.ToString(TagRenderMode.Normal);
        }


        public static string UrlActionButton(this HtmlHelper htmlHelper, string linkText, string url)
        {
            return htmlHelper.UrlActionButton(linkText, url, new RouteValueDictionary());
        }

        public static string UrlActionButton(this HtmlHelper htmlHelper, string linkText, string url, object htmlAttributes)
        {
            return htmlHelper.UrlActionButton(linkText, url, new RouteValueDictionary(htmlAttributes));
        }

        public static string UrlActionButton(this HtmlHelper htmlHelper, string linkText, string url, RouteValueDictionary htmlAttributes)
        {
            TagBuilder builder = new TagBuilder("button");
            builder.InnerHtml = !string.IsNullOrEmpty(linkText) ? HttpUtility.HtmlEncode(linkText) : string.Empty;
            builder.MergeAttributes<string, object>(htmlAttributes);
            builder.MergeAttribute("onclick", "javascript:window.location='" + url + "';return false;");
            return builder.ToString(TagRenderMode.Normal);
        }
        #endregion

        #endregion

        #region Forms

        public static MvcForm BeginForm(this HtmlHelper htmlHelper, string url)
        {
            return htmlHelper.BeginForm(url, null);
        }

        public static MvcForm BeginForm(this HtmlHelper htmlHelper, string url, object htmlAttributes)
        {
            return htmlHelper.BeginForm(url, new RouteValueDictionary(htmlAttributes));
        }

        public static MvcForm BeginForm(this HtmlHelper htmlHelper, string url, IDictionary<string, object> htmlAttributes)
        {
            TagBuilder builder = new TagBuilder("form");
            builder.MergeAttributes<string, object>(htmlAttributes);
            builder.MergeAttribute("action", url);
            builder.MergeAttribute("method", HtmlHelper.GetFormMethodString(FormMethod.Post), true);
            htmlHelper.ViewContext.HttpContext.Response.Write(builder.ToString(TagRenderMode.StartTag));
            return new MvcForm(htmlHelper.ViewContext);
        }

        #endregion

        #region Ajax-ActionLink

        public static string AjaxLink(this AjaxHelper htmlHelper, string url, string linkText, AjaxOptions ajaxOptions)
        {
            return htmlHelper.AjaxLink(linkText, url, new RouteValueDictionary(), ajaxOptions);
        }

        public static string AjaxLink(this AjaxHelper htmlHelper, string linkText, string url, IDictionary<string, object> htmlAttributes, AjaxOptions ajaxOptions)
        {
            TagBuilder builder2 = new TagBuilder("a");
            builder2.InnerHtml = HttpUtility.HtmlEncode(linkText);
            TagBuilder builder = builder2;
            builder.MergeAttributes<string, object>(htmlAttributes);
            builder.MergeAttribute("href", url);

            string str = ajaxOptions.ToJavascriptString();
            string result = string.Format(CultureInfo.InvariantCulture, "Sys.Mvc.AsyncHyperlink.handleClick(this, new Sys.UI.DomEvent(event), {0});", new object[] { str });

            builder.MergeAttribute("onclick", result);
            return builder.ToString(TagRenderMode.Normal);

        }

        public static MvcForm BeginAjaxForm(this AjaxHelper ajaxHelper, string url, AjaxOptions ajaxOptions)
        {
            return ajaxHelper.BeginAjaxForm(url, ajaxOptions, null);
        }

        public static MvcForm BeginAjaxForm(this AjaxHelper ajaxHelper, string url, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes)
        {
            return ajaxHelper.CreateForm(url, ajaxOptions, htmlAttributes);
        }


        private static MvcForm CreateForm(this AjaxHelper ajaxHelper, string url, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes)
        {
            TagBuilder builder = new TagBuilder("form");
            builder.MergeAttributes<string, object>(htmlAttributes);
            builder.MergeAttribute("action", url);
            builder.MergeAttribute("method", "post");
            builder.MergeAttribute("onsubmit", GenerateAjaxScript(GetAjaxOptions(ajaxOptions), "Sys.Mvc.AsyncForm.handleSubmit(this, new Sys.UI.DomEvent(event), {0});"));
            HttpResponseBase response = ajaxHelper.ViewContext.HttpContext.Response;
            response.Write(builder.ToString(TagRenderMode.StartTag));
            return new MvcForm(ajaxHelper.ViewContext);
        }



        private static string GenerateAjaxScript(AjaxOptions ajaxOptions, string scriptFormat)
        {
            string str = ajaxOptions.ToJavascriptString();
            return string.Format(CultureInfo.InvariantCulture, scriptFormat, new object[] { str });
        }

        private static AjaxOptions GetAjaxOptions(AjaxOptions ajaxOptions)
        {
            if (ajaxOptions == null)
            {
                return new AjaxOptions();
            }
            return ajaxOptions;
        }


















        #endregion

        #region Submit button

        public static string SubmitButton(this HtmlHelper htmlHelper, string text)
        {
            return htmlHelper.SubmitButton(text, null);
        }

        public static string SubmitButton(this HtmlHelper htmlHelper, string text, object htmlAttributes)
        {
            return htmlHelper.SubmitButton(text, null, null, new RouteValueDictionary(htmlAttributes));
        }

        public static string SubmitButton(this HtmlHelper htmlHelper, string text, string formId, string formUrl, object htmlAttributes)
        {
            return htmlHelper.SubmitButton(text, formId, formUrl, new RouteValueDictionary(htmlAttributes));
        }

        public static string SubmitButton(this HtmlHelper htmlHelper, string text, string formId, string formUrl, RouteValueDictionary htmlAttributes)
        {
            TagBuilder builder = new TagBuilder("input");
            builder.MergeAttribute("value", text);
            if (!string.IsNullOrEmpty(formUrl) && !string.IsNullOrEmpty(formId))
            {
                builder.MergeAttribute("onclick", "return submitForm('" + formId + "','" + formUrl + "');");
            }
            builder.MergeAttribute("type", "submit");
            builder.MergeAttributes<string, object>(htmlAttributes);
            return builder.ToString(TagRenderMode.Normal);
        }

        #endregion

        #region DropDown

        public static string DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<Control> controls)
        {
            SelectList list = new SelectList(controls, "Id", "Name");
            return htmlHelper.DropDownList(name, list).ToHtmlString();
        }

        public static string DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<Container> containers)
        {
            SelectList list = new SelectList(containers, "Id", "MarkupId");
            return htmlHelper.DropDownList(name, list).ToHtmlString();
        }

        #endregion

        #region Anti forgery token

        public static string MvcEngineAntiForgeryToken<TModel>(this HtmlHelper<TModel> htmlHelper, Func<TModel, AntiForgeryToken> getToken) where TModel : class
        {
            AntiForgeryToken token = getToken(htmlHelper.ViewData.Model);

            if (token == null) return "";

            htmlHelper.ViewContext.HttpContext.Response.Cookies.Add(new System.Web.HttpCookie(AntiForgeryToken.TicksName, token.Ticks.ToString()));

            return string.Format("<input type=\"hidden\" name=\"{0}\" value=\"{1}\" />", AntiForgeryToken.TokenName, token.Value);
        }

        public static string GetAntiForgeryToken<TModel>(this HtmlHelper<TModel> htmlHelper, Func<TModel, AntiForgeryToken> getToken) where TModel : class
        {
            AntiForgeryToken token = getToken(htmlHelper.ViewData.Model);

            if (token == null) return "";

            htmlHelper.ViewContext.HttpContext.Response.Cookies.Add(new System.Web.HttpCookie(AntiForgeryToken.TicksName, token.Ticks.ToString()));

            return token.Value;
        }

        #endregion

        #region Captcha

        public static string Captcha(this HtmlHelper helper)
        {
            return Captcha(helper, CaptchaConsts.DefaultName, null);
        }

        public static string Captcha(this HtmlHelper helper, IDictionary<string, object> htmlAttributes)
        {
            return Captcha(helper, CaptchaConsts.DefaultName, htmlAttributes);
        }

        public static string Captcha(this HtmlHelper helper, object htmlAttributes)
        {

            return Captcha(helper, CaptchaConsts.DefaultName, new RouteValueDictionary(htmlAttributes));
        }

        public static string Captcha(this HtmlHelper helper, string captchaName, IDictionary<string, object> htmlAttributes)
        {
            string key = Guid.NewGuid().ToString();

            int randomTextLength = 5;
            string chars = "ACDEFGHJKLNPQRTUVXYZ2346789";

            object value = GenerateRandomText(randomTextLength, chars);
            CacheManager.Current.AddToCache(key, value, 20);

            string result = "<div class=\"captchaImage\"><img src=\"/captcha.ashx?" + CaptchaConsts.KeyName + "=" + key + "\" alt=\"captcha\"/></div>";
            result += "<div class=\"captchaField\">";
            result += helper.TextBox(captchaName, null, htmlAttributes);
            result += helper.ValidationMessage(captchaName);
            result += helper.Hidden(CaptchaConsts.KeyName, key);
            result += "</div>";
            return result;
        }

        private static string GenerateRandomText(int length, string chars)
        {
            StringBuilder sb = new StringBuilder(length);
            int maxLength = chars.Length;
            Random rand = new Random();
            for (int i = 0; i < length; i++)
            {
                sb.Append(chars.Substring(rand.Next(maxLength), 1));
            }
            return sb.ToString();
        }

        #endregion

        #region Image

        public static string Image(this HtmlHelper htmlHelper, string path, string alt)
        {
            Guard.ThrowIfEmptyString(path);

            return "<img src=\"" + path + "\" alt=\"" + alt + "\" />";
        }

        #endregion

        #region Dialogs

        public static string DeleteConfirmDialog(this HtmlHelper htmlHelper)
        {
            string message = ResourceReader.Current.GetString("DeleteDialogMessage");
            return htmlHelper.ConfirmDialog(message);
        }

        public static string ConfirmDialog(this HtmlHelper htmlHelper, string message)
        {
            string title = ResourceReader.Current.GetString("ConfirmDialogTitle");
            return htmlHelper.ConfirmDialog(title, message);
        }

        public static string ConfirmDialog(this HtmlHelper htmlHelper, string title, string message)
        {
            return "jConfirm('" + message + "', '" + title + "', function(r) { if(r == true) { $('#{0}').submit(); } else { return false; } });";
        }

        #endregion

        #region Notifications

        private static string Notification(this HtmlHelper htmlHelper, string notificationClass, string status, string id, string text)
        {
            TagBuilder div = new TagBuilder("div");
            div.Attributes["id"] = id;
            div.AddCssClass(notificationClass + " " + "notification");
            div.Attributes["style"] = "display: none;";

            TagBuilder p = new TagBuilder("p");
            TagBuilder type = new TagBuilder("div") { InnerHtml = status };
            type.AddCssClass("left notification-msg-type");
            TagBuilder content = new TagBuilder("div") { InnerHtml = text };
            content.AddCssClass("left notification-msg-content");

            p.InnerHtml = type.ToString(TagRenderMode.Normal) + content.ToString(TagRenderMode.Normal);

            div.InnerHtml = p.ToString(TagRenderMode.Normal);

            return div.ToString(TagRenderMode.Normal);
        }

        public static string SuccessNotification(this HtmlHelper htmlHelper, string id, string text)
        {
            string word = ResourceReader.Current.GetString("SuccessNotificationWord");
            return htmlHelper.Notification("notifications", word, id, text);
        }

        public static string ErrorNotification(this HtmlHelper htmlHelper, string id, string text)
        {
            string word = ResourceReader.Current.GetString("ErrorNotificationWord");
            return htmlHelper.Notification("errors", word, id, text);
        }

        #endregion

        #region Paging

        public static string Pager(this HtmlHelper htmlHelper, Func<int, string> expression, int pageCount, int currentPage, string listClass)
        {
            if (pageCount <= 1)
                return string.Empty;

            int pageLinks = 7;

            StringBuilder builder = new StringBuilder();

            builder.AppendFormat("<ul class=\"{0}\">", listClass);

            int startPage = (currentPage - pageLinks / 2);

            if (pageCount - startPage < pageLinks)
                startPage = startPage - (pageLinks - (pageCount - startPage)) + 1;

            for (int index = startPage <= 1 ? 1 : startPage, i = 1; index <= pageCount; index++, i++)
            {
                if (i == 1 && startPage + pageLinks - 1 > pageLinks)
                {
                    builder.AppendFormat("<li>{0}</li>", htmlHelper.UrlActionLink("<", expression(index - 1)));
                }
                if (index == currentPage)
                    builder.AppendFormat("<li><em>{0}</em></li>", index);
                else
                    builder.AppendFormat("<li>{0}</li>", htmlHelper.UrlActionLink(index.ToString(), expression(index)));
                if (i == pageLinks && index != pageCount)
                {
                    builder.AppendFormat("<li>{0}</li>", htmlHelper.UrlActionLink(">", expression(index + 1)));
                    break;
                }
            }
            builder.Append("</ul>");
            return builder.ToString();
        }

        #endregion
    }
}
